<template>
  <div
    v-if="!initializing"
    class="vc-column"
    :class="currNode.children.length === 0 ? 'empty' : ''"
    :style="visual.parseStyle(currNode.currStyle)"
  >
    <div class="vc-row" v-for="(row, index) in currNode.children" :key="index">
      <div
        v-for="(child, index2) in row.children"
        :key="index2"
        class="vc-col"
        :style="'width: ' + child.width + '%'"
      >
        <Component
          v-for="(comp, index3) in child.children"
          :key="index3"
          :ref="el => setRefs(el, comp)"
          :is="comp.compName"
          :node="comp"
        />
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, nextTick, inject, getCurrentInstance } from 'vue';
import { useVisualCompStore } from '@/visual/js/visual-comp';
import visual from "@/visual/js/visual";
import EventHandler from "@/visual/js/event";

const props = defineProps({
  node: Object
});
const initializing = ref(true);
const refs = ref({});
const compName = ref('');
const currNode = ref(props.node);
const visualComp = useVisualCompStore();
const eventHandler = new EventHandler(
  inject('eventHandler', Function, true),
  getCurrentInstance(),
  currNode.value
);

const init = async(styleId) => {
  initializing.value = true;
  visual.initStyle(currNode.value, styleId);

  if (currNode.value.type) {
    const res = await visualComp.getVisualComp(currNode.value.type);
    compName.value = res.name;
  }

  let widths = [];
  let colCnt = parseInt(currNode.value.currCompStyle['col'] || '2');
  if (!currNode.value.currCompStyle['span'] || currNode.value.currCompStyle['span'].split(',') < colCnt) {
    const avgSpan = 100/colCnt;
    for (let index=0; index<colCnt; index++) {
      widths.push(avgSpan);
    }
  } else {
    const spans = [];
    let total = 0;
    for (let item of currNode.value.currCompStyle['span'].split(',')) {
      total += parseInt(item || '1');
      spans.push(parseInt(item || '1'));
    }
    for (let index=0; index<spans.length; index++) {
      widths.push(spans[index]*100/total);
    }
  }

  for (const row of currNode.value.children) {
    for (let index=0; index<row.children.length; index++) {
      const col = row.children[index];
      col.width = widths[index];
      for (const comp of col.children) {
        comp.compName = await getComp(comp.type)
      }
    }
  }
  await nextTick(() => initializing.value = false);
};

const getComp = async(type) => {
  const res = await visualComp.getVisualComp(type);
  return res.name;
}

const setRefs = (el, comp) => {
  if (el) {
    refs.value[comp.id] = el;
  }
};

const validate = (callback) => {
  const promise = new Promise((resolve, reject) => {
    let keys = Object.keys(refs.value);

    if (keys.length === 0) {
      resolve(true);
      return;
    }

    const validates = keys.map(key => {
      const component = refs.value[key];
      if (!component.validate) {
        return Promise.resolve(true);
      }

      return new Promise((innerResolve, innerReject) => {
        component.validate(valid => {
          if (!valid) {
            innerReject();
          } else {
            innerResolve();
          }
        }).catch(innerReject);
      });
    });

    Promise.all(validates)
      .then(() => {
        resolve(true);
      })
      .catch(reject);
  });

  promise.then(valid => callback(valid));
  return promise;
};

const changeStyle = (styleId) => {
  init(styleId);
};

const refresh = async() => {
  initializing.value = true;
  await nextTick(() => initializing.value = false);
};

onMounted(async() => {
  await init();
  await eventHandler.trigger('初始化事件');
});

defineExpose({ validate, changeStyle, refresh });
</script>

<style scoped lang="scss">
.vc-column {
  display: flex;
  flex-direction: column;
  justify-content: start;
  align-items: center;
  flex-grow: 1;
  .vc-row {
    width: 100%;
    flex-grow: 1;
    display: flex;
    flex-direction: row;
    justify-content: start;
    align-items: center;
  }
  .vc-col {
    flex-grow: 1;
    display: flex;
    flex-direction: row;
    justify-content: start;
    align-items: center;
  }
}
</style>